#include "TableOrder.h"
#include "Index.h"
#include <boost/thread/lock_guard.hpp>

class IndexOrderApplyTime : public StringIndex{
public:
    IndexOrderApplyTime()
    :StringIndex("applyTime")
    {
    }
    virtual ~IndexOrderApplyTime(){
    }

    int parseKey(const char * value, std::string & outKey) override {
        uint8_t len = value[0];
        outKey.assign(value+1, len);
        return 0;
    }
};

TableOrder::TableOrder()
:Table("order")
{
}
TableOrder::~TableOrder(){
}

int TableOrder::init() {
    boost::lock_guard<boost::mutex> guard(mutex);

    Index * applyTime = new IndexOrderApplyTime();
    indexes.insert(std::make_pair(applyTime->getName(), applyTime));
    return 0; 
}
void TableOrder::deinit() {
    boost::lock_guard<boost::mutex> guard(mutex);

    auto it = indexes.begin();
    for(; it != indexes.end(); ++it){
        delete it->second;
    }
}


int TableOrder::insert(const int key, const char * value) {
    boost::lock_guard<boost::mutex> guard(mutex);
    //indexes insert
    {
        auto it = indexes.begin();
        for(; it != indexes.end(); ++it){
            std::string indexKey;
            if(0 == it->second->parseKey(value, indexKey)){
                it->second->insert(indexKey, key);
            }
            else{
                //Error: parseKey for index error: index->getName();
                return -1;
            }
        }
    }
    // data insert
    {
        auto it = data.find(key);
        if( it == data.end()){
            data.insert(std::make_pair(key, value));
            return 0;
        }
    }
    return -1;
}
int TableOrder::update(const int key, const char * value) {
    boost::lock_guard<boost::mutex> guard(mutex);
    {
        auto it = indexes.begin();
        for(; it != indexes.end(); ++it){
            std::string indexKey;
            if(0 == it->second->parseKey(value, indexKey)){
                if( 0 != it->second->update(indexKey, key)){
                    return -1;
                }
            }
            else{
                //ParseKey  of index (name) error
                return -1;
            }
        }
    }
    
    //data update
    {
        auto it = data.find(key);
        if( it != data.end()){
            it->second = value;
            return 0;
        }
    }
    return -1;
}
int TableOrder::remove(const int key) {
    boost::lock_guard<boost::mutex> guard(mutex);
    auto it = data.find(key);
    if(it != data.end()){
        const char * value =  it->second;
        {
            auto indexIt = indexes.begin();
            for(; indexIt != indexes.end(); ++indexIt){
                std::string indexKey;
                if(0 == indexIt->second->parseKey(value, indexKey)){
                    if( 0 != indexIt->second->remove(indexKey, key)){
                        return -1;
                    }
                }
                else{
                    //ParseKey  of index (name) error
                    return -1;
                }
            }
        }
        data.erase(it);
    }
    return 0;
}


const char * TableOrder::find(const int key) const {
    boost::lock_guard<boost::mutex> guard(mutex);
    auto it = data.find(key);
    if( it != data.end()){
        return it->second;
    }
    return nullptr;
}
Records * TableOrder::rangeScan(int offset, int count, bool isAscend, const std::string & indexName) const{
    boost::lock_guard<boost::mutex> guard(mutex);
    auto it = indexes.find(indexName);
    if(it != indexes.end()){
        if(nullptr != it->second){
            auto pkArray = it->second->rangeScan(offset, count, isAscend);
            if(pkArray != nullptr){
                Records * ret = new Records();
                for(auto e : *pkArray){
                    ret->pushBack(e, data.at(e));
                }
                return ret;
            }
            return nullptr;
        }
        //Error index(name) is nullptr
    }
    //Error no this index(name)
    return nullptr;
}

std::string TableOrder::toString() const {
    boost::lock_guard<boost::mutex> guard(mutex);
    std::ostringstream oss;

    oss << "TableOrder: "
        << " size:" << data.size() << std::endl;
    oss << " Data:";
    auto it = data.begin();
    for(; it != data.end(); ++it){
        oss << "\tk:" << it->first  << " v:" << std::hex << (uint64_t)(it->second) << std::dec <<std::endl;
    }
    oss << std::endl;

    oss << " Index:";
    auto indexIt = indexes.begin();
    for(; indexIt != indexes.end(); ++indexIt){
        oss << indexIt->second->toString() << std::endl;
    }
    return oss.str();
}



#include "Util.h"
void TableOrder::test(){
    TableOrder order;
    order.init();

    order.insert(1, Util::make("hello"));
    order.insert(2, Util::make("hello1"));
    order.insert(3, Util::make("hello2"));
    order.insert(4, Util::make("hello"));
    order.insert(5, Util::make("hello1"));
    order.insert(6, Util::make("hello"));
    order.insert(7, Util::make("hello2"));

    std::cout << order.toString() << std::endl;

    auto func = [&order](int offset, int count, bool isAscend, const std::string & indexName){
        std::cout << "rangeScan:("<< offset<<","<<count<<","<<isAscend<<","<<indexName <<")";
        auto records = order.rangeScan(offset, count, isAscend, indexName);
        if(records != nullptr){
            std::cout << records->toString() << std::endl;
            delete records;
        }
        else{
            std::cout << "records is nullptr" << std::endl;
        }
    };
    func(0, 2, true, "applyTime");
    func(0, 7, true, "applyTime");

    func(0, 0, true, "applyTime");
    func(1, 1, true, "applyTime");
    func(2, 1, true, "applyTime");
    func(2, 0, true, "applyTime");

    func(3, 1, true, "applyTime");
    func(4, 1, true, "applyTime");

    func(5, 1, true, "applyTime");
    func(6, 1, true, "applyTime");

    func(7, 1, true, "applyTime");
    func(7, 0, true, "applyTime");

    func(2, 10, true, "applyTime");

    // desc orderApplyTime RangeScan
    func(0, 2, false, "applyTime");
    func(0, 7, false, "applyTime");

    func(0, 0, false, "applyTime");
    func(1, 1, false, "applyTime");
    func(2, 1, false, "applyTime");
    func(2, 0, false, "applyTime");

    func(3, 1, false, "applyTime");
    func(4, 1, false, "applyTime");

    func(5, 1, false, "applyTime");
    func(6, 1, false, "applyTime");

    func(7, 1, false, "applyTime");
    func(7, 0, false, "applyTime");

    func(2, 10, false, "applyTime");

    order.deinit();
}
